assert(TrackCooldowns, "TrackCooldowns not found!")

TC = {
	VersionBuild = "2",
	VersionMinor = "6",
	VersionMajor = "4",
}

local AceConfig = LibStub("AceConfigDialog-3.0")
local Media = LibStub("LibSharedMedia-3.0")
local L = LibStub("AceLocale-3.0"):GetLocale("TrackCooldowns")

local TrackCooldowns = TrackCooldowns
local mod = TrackCooldowns:NewModule("Report", nil, "AceComm-3.0", "AceEvent-3.0", "AceConsole-3.0", "TCLibBars-1.0")
local ScrollingTable = LibStub("ScrollingTable")
local LibDialog = LibStub("LibDialog-1.0")

local pairs = _G.pairs
local UnitName, UnitClass = _G.UnitName, _G.UnitClass
local tonumber, tostring, type = _G.tonumber, _G.tostring, _G.type

local shiftOnNextCD
local shownReportIndex = 1
local reportMax = 10
local reports = {}
local reportNames = {
	[1] = { }
}
local frame
local titleFrame
local scrollTable
local inBossFight

local cols = {
	{ 
		["name"] = "Player",
		["width"] = 200,
		["align"] = "LEFT",
		["defaultsort"] = "dsc",
		["sort"] = "dsc",
		["sortnext"] = 3,
	},
	{
		["name"] = "Spell Name",
		["width"] = 185,
		["align"] = "LEFT",
		["defaultsort"] = "dsc",
	},
	{
		["name"] = "Usage Count",
		["width"] = 100,
		["align"] = "CENTER",
		["sortnext"] = 2,
	},
}

local TrackCooldowns_Report_Menu = CreateFrame("Frame", "TrackCooldowns_Report_Menu")
TrackCooldowns_Report_Menu.displayMode = "MENU"
TrackCooldowns_Report_Menu.info = {}
TrackCooldowns_Report_Menu.initialize = function(self, level)
    if not level then return end
    wipe(info)
    if level == 1 then
        -- Create the title of the menu
        info.isTitle = 1
        info.text = "Fights"
        info.notCheckable = 1
        UIDropDownMenu_AddButton(info, level)

        info.disabled     = nil
        info.isTitle      = nil
        info.notCheckable = nil

		info.text = "Total"
		info.func = function()
			shownReportIndex = 0
			mod:UpdateTable()
		end
		if 0 == shownReportIndex then
			info.checked = 1
		else
			info.checked = nil
		end
		UIDropDownMenu_AddButton(info, level)
		
		info.text = "Current Fight"
		info.func = function()
			shownReportIndex = 1
			mod:UpdateTable()
		end
		if 1 == shownReportIndex then
			info.checked = 1
		else
			info.checked = nil
		end
		UIDropDownMenu_AddButton(info, level)
	
		local i
		for i = 2, reportMax do
			if reports[i] then
				if reportNames[i] and reportNames[i].name then
					info.text = reportNames[i].name .. " (" .. date("%H:%M:%S", reportNames[i].startTime) .. " - " .. date("%H:%M:%S", reportNames[i].endTime) .. ")"
				elseif reportNames[i] then
					info.text = date("%H:%M:%S", reportNames[i].startTime) .. " - " .. date("%H:%M:%S", reportNames[i].endTime)
				else
					info.text = "Fight " .. i
				end
				
				info.func = function()
					shownReportIndex = i
					mod:UpdateTable()
				end
				if i == shownReportIndex then
					info.checked = 1
				else
					info.checked = nil
				end
				UIDropDownMenu_AddButton(info, level)
			end
		end
    end
end

LibDialog:Register("TrackCooldowns_ReportReset", {
	text = "Are you sure you want to reset the report screen?",
	buttons = {
		{
			text = _G.YES,
			on_click = function(self, data)
				mod:ResetReports()
			end,
		},
		{
			text = _G.NO,
		},
	},
	show_while_dead = true,
	hide_on_escape = true,
})

function mod:OnInitialize()
end

function mod:OnEnable()
	self.db = LibStub("AceDB-3.0"):New("TC_DB", nil, "Default")
	self:RegisterEvent("PLAYER_REGEN_DISABLED")
	self:RegisterEvent("PLAYER_REGEN_ENABLED")	
	
	-- Check boss mods for encounter engagement
	local function DbmTCCallbackReport(event, dbmModule)
		--TrackCooldowns:Print("Report: Kill/Wipe detected by DBM, reset")
		if "pull" == event then
			inBossFight = true
			reportNames[1].name = dbmModule.localization.general.name
			reportNames[1].startTime = time()
		elseif "wipe" == event or "kill" == event then
			reportNames[1].endTime = time()
			inBossFight = nil
			shiftOnNextCD = true
		end
	end
	
	if DBM then
		TrackCooldowns:Print(L["Using %s for boss kill tracking"]:format("DBM"))
		DBM.RegisterCallback(mod, "pull", DbmTCCallbackReport)
		DBM.RegisterCallback(mod, "wipe", DbmTCCallbackReport)	
		DBM.RegisterCallback(mod, "kill", DbmTCCallbackReport)			  
	elseif BigWigsLoader then
		TrackCooldowns:Print(L["Using %s for boss kill tracking"]:format("BigWigs"))
		BigWigsLoader.RegisterMessage(mod, "BigWigs_OnBossEngage", function(event, bwMod)
			--TrackCooldowns:Print("Report: Pull detected by BigWigs")
			inBossFight = true
			reportNames[1].name = bwMod.moduleName
			reportNames[1].startTime = time()
		end)
		BigWigsLoader.RegisterMessage(mod, "BigWigs_OnBossWin", function(event, bwMod)
			--TrackCooldowns:Print("Report: Kill detected by BigWigs")
			reportNames[1].endTime = time()
			inBossFight = nil
			shiftOnNextCD = true
		end)
		BigWigsLoader.RegisterMessage(mod, "BigWigs_OnBossWipe", function(event, bwMod)
			--TrackCooldowns:Print("Report: Wipe detected by BigWigs")
			if inBossFight then
				reportNames[1].endTime = time()
				inBossFight = nil
				shiftOnNextCD = true
			end
		end)
	--[[elseif DXE then
		TrackCooldowns:Print(L["Using %s for boss kill tracking"]:format("DXE"))
		DXE.RegisterCallback(TrackCooldowns, "StartEncounter", dxeTCCallback)]]
	end
end

function mod:OnDisable()
end

function mod:ShowReport()
	if not frame then
		mod:CreateFrame()
	else
		frame:Show()
		titleFrame:Show()
	end
end

function mod:CreateFrame()
	frame = CreateFrame("Frame", "TCReportUIFrame", UIParent)
	frame:SetWidth(540)
    frame:SetPoint("CENTER",UIParent,"CENTER",0,0)
    frame:SetPoint("TOP",UIParent,"CENTER",0,4*(15+68)/2)
  	frame:EnableMouse()
    frame:SetScale(1)
    frame:SetResizable()
    frame:SetMovable(true)
    frame:SetFrameStrata("HIGH")
    frame:SetToplevel(true)
    frame:SetBackdrop({
		bgFile = "Interface\\DialogFrame\\UI-DialogBox-Gold-Background",
		edgeFile = "Interface\\Tooltips\\UI-Tooltip-Border",
		tile = true, tileSize = 64, edgeSize = 12,
		insets = { left = 2, right = 1, top = 2, bottom = 2 }
    })
    frame:SetBackdropColor(1,1,0,1)
    frame:SetBackdropBorderColor(1,1,1,0.2)
	frame:SetScript("OnMouseDown", function() frame:StartMoving() end)
    frame:SetScript("OnMouseUp", function() frame:StopMovingOrSizing() end)
	
	titleFrame = CreateFrame("Frame", nil, UIParent)
    titleFrame:SetBackdrop({
		bgFile = "Interface\\ChatFrame\\ChatFrameBackground",
		edgeFile = "Interface\\Tooltips\\UI-Tooltip-Border",
		tile = true, tileSize = 64, edgeSize = 12,
		insets = { left = 2, right = 1, top = 2, bottom = 2 }
    })
	titleFrame:SetFrameLevel(frame:GetFrameLevel() + 1)
	titleFrame:SetFrameStrata("DIALOG")
    titleFrame:SetBackdropColor(0, 0, 0, 1)
    titleFrame:SetHeight(22)
    titleFrame:EnableMouse()
    titleFrame:EnableMouseWheel(true)
    titleFrame:SetResizable()
    titleFrame:SetMovable(true)
    titleFrame:SetPoint("LEFT", frame, "TOPLEFT", 20, 0)
    titleFrame:SetPoint("RIGHT", frame, "TOPRIGHT", -20, 0)
    titleFrame:SetScript("OnMouseDown", function() frame:StartMoving() end)
    titleFrame:SetScript("OnMouseUp", function() 
		frame:StopMovingOrSizing()
		if frame.lastClick and GetTime() - frame.lastClick <= 0.5 then
            if frame:IsShown() then
                frame:Hide()
            else
                frame:Show()
            end
            frame.lastClick = nil;
        else
            frame.lastClick = GetTime();
        end
	end)
	
    local titleText = titleFrame:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall")
    titleText:SetPoint("CENTER", titleFrame, "CENTER", 0, 1)
    titleText:SetText("TrackCooldowns Usage Report")
	titleFrame.titleText = titleText
    frame.titleFrame = titleFrame
	
	local filterText = frame:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall")
    filterText:SetPoint("LEFT", frame, "TOPLEFT", 10, -25)
    filterText:SetText("Search:")
	frame.filterText = filterText
	
	local filterBox = CreateFrame("EditBox", "TCReportFilter", frame, "InputBoxTemplate")
	filterBox:SetPoint("LEFT", filterText, "TOPRIGHT", 10, -5)
	filterBox:SetAutoFocus(false)
	filterBox:SetWidth(125)
	filterBox:SetHeight(10)
	filterBox:SetScript("OnTextChanged", function(editBox, isUserInput)
		scrollTable:SetFilter(function(self, rowdata)
			if rowdata.cols then
				for col, value in pairs(rowdata.cols) do
					if value and type(value) == "table" and value.value then
						if type(value.value) == "string" and string.find(value.value:lower(), editBox:GetText():lower()) then
							return true
						elseif type(value.value) == "number" and tostring(value.value) == editBox:GetText() then
							return true
						end
					end
				end
			end
			return false
		end)
	end)
	filterBox:SetScript("OnEnterPressed", function(editBox)
		editBox:ClearFocus()
	end)
	filterBox:SetScript("OnEscapePressed", function(editBox)
		editBox:ClearFocus()
	end)
	frame.filterBox = filterBox
	
	local trackedOnlyBox = CreateFrame("CheckButton", "TCTrackedOnly", frame, "UICheckButtonTemplate")
	_G[trackedOnlyBox:GetName() .. "Text"]:SetText("Show tracked CDs only")
	trackedOnlyBox:SetChecked(self.db.profile.reportOnlyTracked)
	trackedOnlyBox:SetPoint("RIGHT", frame, "TOPRIGHT", (trackedOnlyBox:GetWidth() * -5.0) + 20, (trackedOnlyBox:GetHeight() * -1.0) + 6)
	trackedOnlyBox:SetScript("OnClick", function()
		if not self.db.profile.reportOnlyTracked then
			self.db.profile.reportOnlyTracked = true
		else
			self.db.profile.reportOnlyTracked = false
		end
		mod:UpdateTable()
	end)
	frame.trackedOnlyBox = trackedOnlyBox
	
	local bossesOnly = CreateFrame("CheckButton", "TCBossesOnly", frame, "UICheckButtonTemplate")
	_G[bossesOnly:GetName() .. "Text"]:SetText("Show boss fights only")
	bossesOnly:SetChecked(self.db.profile.showBossesOnly)
	bossesOnly:SetPoint("RIGHT", trackedOnlyBox, "LEFT", (frame:GetWidth() / -5.0) - 10, 0)
	bossesOnly:SetScript("OnClick", function()
		if not self.db.profile.showBossesOnly then
			self.db.profile.showBossesOnly = true
		else
			self.db.profile.showBossesOnly = false
		end
		mod:UpdateTable()
	end)
	frame.bossesOnly = bossesOnly
	
	local reportButton = CreateFrame("Button", "TCReportButton", frame, "UIPanelButtonTemplate")
	reportButton:SetWidth(110)
	reportButton:SetHeight(22)
	reportButton:SetText("Select Report")
	reportButton:SetPoint("LEFT", frame, "TOPLEFT", 10, (frame:GetHeight() - reportButton:GetHeight() + 5) * -1.0)
	reportButton:SetScript("OnClick", function()
		ToggleDropDownMenu(1, nil, TrackCooldowns_Report_Menu, reportButton, 0, -5);
	end)
	frame.reportButton = reportButton
	
	local resetButton = CreateFrame("Button", "TCResetButton", frame, "UIPanelButtonTemplate")
	resetButton:SetWidth(110)
	resetButton:SetHeight(22)
	resetButton:SetText("Reset Reports")
	resetButton:SetPoint("LEFT", frame, "TOPLEFT", (frame:GetWidth() / 2 - resetButton:GetWidth() / 2), (frame:GetHeight() - resetButton:GetHeight() + 5) * -1.0)
	resetButton:SetScript("OnClick", function()
		LibDialog:Spawn("TrackCooldowns_ReportReset")
	end)
	frame.resetButton = resetButton
	
	local closeButton = CreateFrame("Button", "TCResetButton", frame, "UIPanelButtonTemplate")
	closeButton:SetWidth(110)
	closeButton:SetHeight(22)
	closeButton:SetText("Close")
	closeButton:SetPoint("RIGHT", frame, "TOPLEFT", frame:GetWidth() - 10 , (frame:GetHeight() - closeButton:GetHeight() + 5) * -1.0)
	closeButton:SetScript("OnClick", function()
		frame:Hide()
		titleFrame:Hide()
		CloseDropDownMenus()
	end)
	frame.closeButton = closeButton
	
	scrollTable = ScrollingTable:CreateST(cols, 16, nil, nil, frame);
	scrollTable.frame:SetPoint("TOPLEFT", frame, "TOPLEFT", 10, -30 + reportButton:GetHeight() * -1.0)
	frame.scrollTable = scrollTable
	
	mod:UpdateTable()
end

function mod:IncrementCooldown(sender, spellId)
	local name = GetSpellInfo(spellId)
	
	if not inBossFight then
		if shiftOnNextCD and reports[1] and next(reports[1]) then
			reportNames[1].endTime = time()
			mod:ShiftStoredReports()
			shiftOnNextCD = nil
		else
			shiftOnNextCD = nil
			reportNames[1].name = ""
		end
	end
	
	if not reports[1] then
		reports[1] = { }
	end
	
	if not reportNames[1] then
		reportNames[1] = { }
	end
	
	if not reportNames[1].startTime then
		reportNames[1].startTime = time()
	end
	
	local reportRow = reports[1]
	local isUpdated = nil
	if reportRow[sender] then
		if reportRow[sender][name] then
			if reportRow[sender][name].timeCast < time() then
				reportRow[sender][name].spellId = spellId
				reportRow[sender][name].count = reportRow[sender][name].count + 1
				reportRow[sender][name].timeCast = time()
				isUpdated = true
			end
		else
			reportRow[sender][name] = {
				spellId = spellId,
				count = 1,
				timeCast = time(),
			}
			isUpdated = true
		end
	else
		local _, c = UnitClass(sender)
		reportRow[sender] = {
			[name] = {
				spellId = spellId,
				count = 1,
				timeCast = time(),
			},
			["class"] = c,
		}
		isUpdated = true
	end

	if frame and scrollTable and isUpdated then
		self:UpdateTable()
	end
end

function mod:UpdateTable()
	local tableData = {}
	
	if scrollTable then
		if reports[shownReportIndex] then
			for playerName, spells in pairs(reports[shownReportIndex]) do
				for spellName, spellData in pairs(spells) do
					if spellName ~= "class" and (not self.db.profile.reportOnlyTracked or (self.db.profile.reportOnlyTracked and self.db.profile.spells[spellData.spellId])) then
						local color = RAID_CLASS_COLORS[spells["class"]]
						if type(color) ~= "table" then
							color.r = 1.0
							color.g = 1.0
							color.b = 1.0
							color.a = 1.0
						end
						
						local row = {
							["cols"] = { 
								{ 
									["value"] = playerName, 
									["color"] = { 
										["r"] = color.r, 
										["g"] = color.g, 
										["b"] = color.b, 
										["a"] = 1 
									} 
								},
								{ ["value"] = spellName },
								{ ["value"] = spellData.count },
							}
						}

						table.insert(tableData, row)
					end
				end
			end
			scrollTable:SetData(tableData)
			scrollTable:SortData()
		elseif shownReportIndex == 0 then
			for reportNum, report in pairs(reports) do
				if not self.db.profile.showBossesOnly or (self.db.profile.showBossesOnly and reportNames[reportNum] and reportNames[reportNum].name and mod:trim(reportNames[reportNum].name) ~= "") then
					for playerName, spells in pairs(report) do
						for spellName, spellData in pairs(spells) do
							if spellName ~= "class" and (not self.db.profile.reportOnlyTracked or (self.db.profile.reportOnlyTracked and self.db.profile.spells[spellData.spellId])) then
								local isInTable
								for rowNum, row in pairs(tableData) do
									if row["cols"][1].value == playerName and row["cols"][2].value == spellName then
										isInTable = true
										row["cols"][3].value = row["cols"][3].value + 1
									end
								end
								
								if not isInTable then
									local color = RAID_CLASS_COLORS[spells["class"]]
									if type(color) ~= "table" then
										color.r = 1.0
										color.g = 1.0
										color.b = 1.0
										color.a = 1.0
									end
									
									local row = {
										["cols"] = { 
											{ 
												["value"] = playerName, 
												["color"] = { 
													["r"] = color.r, 
													["g"] = color.g, 
													["b"] = color.b, 
													["a"] = 1 
												} 
											},
											{ ["value"] = spellName },
											{ ["value"] = spellData.count },
										}
									}

									table.insert(tableData, row)
								end
							end
						end
					end
				end
			end
			scrollTable:SetData(tableData)
			scrollTable:SortData()
		else
			scrollTable:SetData(tableData)
			scrollTable:SortData()
		end
	end
end

function mod:ShiftStoredReports()
	local i
	for i = #reports, 1, -1 do
		if i < reportMax then
			if reports[i] then
				reports[i + 1] = reports[i]
				reportNames[i + 1] = reportNames[i]
			end
		
			if i == 1 then
				reports[i] = nil
				reportNames[i] = nil
			end
		end
	end
	
	mod:UpdateTable()
end

function mod:ResetReports()
	wipe(reports)
	mod:UpdateTable()
end

function mod:PLAYER_REGEN_ENABLED()
	if not reportNames[1] then
		reportNames[1] = { }
	end
	if not inBossFight then
		shiftOnNextCD = true
		reportNames[1].endTime = time()
	end
end

function mod:PLAYER_REGEN_DISABLED()
	if reports[1] and next(reports[1]) and not inBossFight then
		reportNames[1].endTime = time()
		mod:ShiftStoredReports()
	end
	
	if not reportNames[1] then
		reportNames[1] = { }
	end
	
	if not inBossFight then
		reportNames[1].startTime = time()
	end
end

function mod:trim(s)
	return s:find'^%s*$' and '' or s:match'^%s*(.*%S)'
end